-
Notifications
You must be signed in to change notification settings - Fork 11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rewrite the maths & uniform API #43
Rewrite the maths & uniform API #43
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey, thanks for this! I think it does simplify things a lot and I think you're right that 3x3 CPU math can be done in plain Rust, or coerced into PICA types for GPU math.
One use case I'm not sure this would support (which maybe the Uniform
trait would have) is a custom type that's larger than the default types, e.g.
struct BoolVec([bool; 4]);
struct Mat5x5([[f32; 5]; 5]);
struct CustomIVec([u8; 6])
;
I guess maybe the way to handle this kind of thing would be to have a helper function on the type itself, so you'd own a range of indices and call bind
multiple times to bind one of these custom types?
Overall I think this change looks reasonable! I had a couple small comments about transmute
and stuff like that, but generally I think it makes sense and I'm not opposed to simplifying the API a bit and starting integration with some more common Rust graphics / math libs too. Thanks!
// UNWRAP: M ≤ 4, so slicing to a smaller array should always work | ||
rows[..M].try_into().unwrap() | ||
} | ||
pub fn as_raw(&self) -> &citro3d_sys::C3D_Mtx { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: doc comment would be nice. Also, do we really need both into_raw
and as_raw
? I think since C3D_Mtx
is Copy
we should be able to get away with just one or the other...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as_raw
because if we want to pass to the C API we need a reference to the underlying C3D_Mtx
. If we're just passing constant pointers which never get held anywhere (which I think might be the case actually) this is fine, but its a potential footgun that I didn't want to think too hard about. I can make it only as_raw(self) -> citro3d_sys::C3D_Mtx
if you like?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess I don't have a strong opinion either way on this, I would think that as_raw() -> *const citro3d_sys::C3D_Mtx
might be more "universal" since you could just deref to make a copy?
pub struct Index(u8); | ||
|
||
impl From<i8> for Index { | ||
fn from(value: i8) -> Self { | ||
impl From<u8> for Index { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm fine with changing the representation to u8
here, but I guess maybe we should implement TryFrom
to make sure the index is always in the range 0..=i8::MAX
? Not sure what the behavior would be if you passed a negative value or something to the uniform APIs...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can't because it'd be caught by the checks in Uniform::bind
, its u8
cause that makes more sense for an index, <0
isn't valid for them and is only returned by the functions in the C API so it can report error
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess I'm wondering what would happen if someone did let i = Index::from(200)
and then tried to use that index later on in a bind
call... Maybe we should remove this From
impl entirely and make it so the only way to get an Index
is via Program::get_uniform
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess I'm wondering what would happen if someone did
let i = Index::from(200)
and then tried to use that index later on in abind
call... Maybe we should remove thisFrom
impl entirely and make it so the only way to get anIndex
is viaProgram::get_uniform
.
yeah it'd panic on the bind call
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
actually I think I can make this even safer by pulling the valid register range out of the shader program in get uniform and putting it in index. this would let us assert and prevent binding mismatching uniforms (e.g. trying to bind a 4x4 to a 1x4). should I add that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That sounds great! The more safe we can make the APIs the better, in my opinion
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah tried this uh something is going wrong with parsing the shader file but it seems to work weirdly and I can't work it out so I'm going to leave it for now, can always be done later (assuming we keep Index a black box it won't be breaking)
So originally I had the float arm of For stuff like "custom uniforms" the only uniform types that are supported by citro3d C API is bool, ivec, and fvec the fvec version of which has the 1,2,3,4 versions |
Yeah, I guess that's true — I had been hoping it might be possible to implement truly custom uniform types but maybe that's not feasible without implementing everything down to the GPU register writes themselves (like https://github.com/devkitPro/citro3d/blob/master/source/uniforms.c#L54). I guess let's consider this "out of scope" for now, maybe implementing custom registers is possible but if so it could probably just be another enum variant, so we can deal with that later on.
From what I can tell, there isn't necessarily a limit since I think having separate enum variants is fine for now, we could always change it later if it seems like having 5x4 or whatever is useful. |
Yeah I think at that point we'd want an entirely new rust-native library for using the GPU, which I've been considering writing for a bit now (given the amount of footguns involved in all the state and globals in citro3d) but that's too much time investment for me right now :p |
@0x00002a I think everything looks good to me, are you planning on making any additional changes or should I go ahead and merge this if it passes CI checks? |
Yeah I'm good with this changeset |
So theres quite a lot here and I'm aware it might not fly as its a substantial change, though it is mostly non-breaking it does remove some functionality.
Matrix and Matrix3 are gone. My thought process is that there are not many use-cases for the 3x3 stuff since basically all you can use it for is CPU math which the rust ecosystem already has stuff for.
Matrix4
's API has been made more explicit about the row order and has been madeCopy
, also it no longer gives out pointers to the inner as that's just asking for trouble. If unsafe code wants to cast the reference to a pointer that's its business.Uniform is now an enum rather than a sealed trait. This gives us some nice things:
From<MyType> for Uniform
also I added the missing bindings and fixed the soundness issue where an overflow could occure
glam interop with
From
impl's forFVec
,Matrix4
, andUniform
(which lets you bind glam mat4's directly as uniforms), this should help with that CPU math